A type-safe Parse Server SDK for Dart and Flutter. Models are plain immutable classes, a code generator writes their query fields and update ops, and the compiler rejects a wrong field or type before a request is sent. Pure Dart: it runs in Flutter, on the web and on the server, and depends only on http and web_socket_channel.
Tested against parse-server 9.9.0 on MongoDB and on Postgres.
| Package | What it is |
|---|---|
parse_next |
The SDK. Start with its README. |
parse_next_generator |
The build_runner generator, a dev dependency. |
parse_next_it |
Integration tests against real servers; not published. |
Grouped as the Parse JavaScript guide is, so it can be read side by side with it. Checked items are in the SDK and tested, most against both databases in parse_next_it and the rest, such as the distance helpers, in unit tests. Unchecked items are not in the SDK, with the reason.
The master key is for Dart code that runs on a server; an app must not hold it.
- Connect with server URL, app id and client key —
ParseClient(server:, appId:, clientKey:) - An
http.Clientof your own, a request timeout - One installation id per install —
ParseClient(installationId:) - Retries, opt-in —
attemptsfor reads;idempotencyfor writes, which uses the server'sidempotencyOptions - Master key, for code on a server —
ParseClient(masterKey:), sent on every request and on the live query connection
- Typed classes —
@ParseClass('Post')and the generator - Save a new object —
collection.create(value, acl:) - Your own object id —
create(value, id:), with the server'sallowCustomObjectId - Cloud Code context —
context:oncreate,update,replace,deleteandbatch.send - Retrieve by id —
collection.get(id), orquery().include(...).get(id) - Update with Parse ops, applied atomically —
collection.update(id, (o) => [...]) - The object as the server has it after an update, trigger changes included —
UpdateResult.applyTo(doc) - Overwrite every field —
collection.replace(id, value) - Counters —
o.likes.increment(n), negative to decrement - Arrays —
set,add,addUnique,remove - One key of an object field —
o.meta.setKey,incrementKey,unsetKey: one top-level key, once per map in an update - Remove a field —
o.field.unset() - Delete —
collection.delete(id) - Several writes in one request —
client.batch(), 20 to a request, or atomically withsend(transaction: true) - Save nested new objects in one call — save each, then point to it
- Save offline (
saveEventually,destroyEventually) — no local store
- String, Number (
int,double,num), Boolean, Date, enum (stored by name) - Array of String, Number or Boolean, and of Pointers
- Array of Dates, GeoPoints, Files or Objects —
List<DateTime>,List<GeoPoint>,List<ParseFile>,List<Map<String, Object?>>. In a list, a file is matched by its URL too, and on Mongo an object matches only with its keys in the same order - Object —
Map<String, Object?>, holdingDateTime,GeoPoint,PolygonandParseFileat any depth - Pointer, Relation, File, GeoPoint, Polygon, Bytes (
Uint8List) - Null and absent — a nullable field
- An Array of embedded typed classes, such as
List<Bucket>— useList<Map<String, Object?>>
- Find, first, count, find and count in one request —
find,first,count,findAndCount -
equalTo,notEqualTo,lessThan(OrEqualTo),greaterThan(OrEqualTo)—eq,ne,lt,lte,gt,gte -
containedIn,notContainedIn—oneOf,noneOf -
exists,doesNotExist—exists(),exists(false) -
limit,skip,ascending,descendingover several keys —limit,skip,orderBy(f.x.asc) - Every match, page by page (
each,eachBatch,findAll) —stream() - Rows that fail to decode, without failing the page —
tryFind() -
select,exclude— partial:raw(keys:, exclude:)returns untyped rows, since a partial object does not decode into a model with required fields - Conditions inside an Object field —
f.meta.at('a.b') -
hint,comment,readPreference - More than ~1,000 ids in a condition — queries are sent in the body, never in the URL
-
explain,aggregate,distinct—query.explain(),collection.aggregate(pipeline),query.distinct((f) => f.x), undecoded; need the master key - Local vs. server data — no local store
-
equalToon an array,containsAll,containedInon an array —contains,containsAll,containsAny -
containedBy,containsAllStartingWith
-
startsWith,endsWith,contains(escaped),matches(regex, case-insensitive) - Full-text search with language, case and diacritic options —
fullText - Sort by text score —
orderByTextScore()
- On a pointer:
equalTo,notEqualTo,containedIn,notContainedIn—eq,ne,oneOf,noneOf,isId -
matchesQuery,doesNotMatchQuery -
matchesKeyInQuery,doesNotMatchKeyInQuery—matchesKey,notMatchesKey - Objects related through a Relation —
relationField.of(ownerId) - Objects whose Relation holds a member —
relationField.has(pointer) -
include, nested includes,includeAll—include((f) => f.author.then((a) => a.team))
- OR, AND, NOR —
|,&,~
- Subscribe —
query.live(), aStream - create, update, enter, leave and delete events —
Created,Updated,Entered,Left,Deleted - Open and close —
Subscribedafter every (re)connect,Disconnectedwhen the connection drops - Errors — a server error for the subscription ends the stream with its
ParseException - Unsubscribe — cancel the stream; close the socket —
client.close() - A LiveQuery server on another URL —
ParseClient(liveQueryServer:) - Reconnect — automatic, with backoff; never ends the stream
- Session token — the session at the time of listening
- Only updates to some fields (
watch) —live(watch: (f) => [...]) - Only some fields in events (
keys) — a partial object does not decode into the model
- Sign up, log in, log out —
auth.signUp,logIn,logOut - Current user —
auth.current(),auth.me(), andauth.changes - Set the current user from a session token —
auth.become - Keep the session between runs — your
SessionStore, such as secure storage, which covers encrypting it - Verify emails —
auth.requestEmailVerification, andemailVerifiedas a model field - Reset password —
auth.requestPasswordReset - Change password, keeping this session —
auth.updatePassword - Check a password without logging in —
auth.verifyPassword - Query users and point to them — a
_Usermodel in acollection - Log in with a provider (Google, Apple, Facebook, anonymous, ...) —
auth.logInWith(provider, authData) - Link and unlink a provider —
auth.linkWith,auth.unlink - Provider SDK modules that fetch
authDatafor you — get it from the provider's own package
- The current session's properties —
auth.session(): id,expiresAt,installationId,createdWith - Invalid session token —
InvalidSession(209); the stored session is cleared
- Roles as a model —
@ParseClass('_Role')withusersandrolesrelations - Role-based access —
Acl().withRole(name) - Role hierarchy — the
rolesrelation
- Upload —
client.uploadFile(bytes, name:, contentType:), with progress and cancel - Store in an object field —
ParseFile - Download —
client.downloadFile(file) - Metadata and tags —
uploadFile(metadata:, tags:) - Delete —
client.deleteFile(file); needs the master key
-
GeoPoint,Polygon, distances —radiansTo,kilometersTo,milesTo,Polygon.containsPoint -
near, with a maximum distance —near(point, maxKm:) -
withinKilometerswithout sorting —withinKm; miles and radians: convert -
withinGeoBox,withinPolygon,polygonContains—withinBox,withinPolygon,containsPoint - The device's location (
GeoPoint.current) — use a location package
- Pinning, local queries, cached results — not supported
- An installation id on every request
- Send a push to channels or to an
_Installationquery —client.push(data, channels:),push(data, where:); needs the master key and a push adapter on the server - An
_Installationtype — model it with@ParseClass('_Installation') - Scheduled pushes and expiry (
push_time,expiration_time) — not supported
- Retrieve config —
client.config();masterKeyOnlyparams are sent only with the master key - Save config —
client.saveConfig(params, masterKeyOnly:); needs the master key - A cached current config — keep the result yourself
- Custom events — not supported
- Call functions with typed parameters and results —
@ParseFunctions - Start a job —
client.startJob(name, params), returning its_JobStatusid; needs the master key
- Schemas, indexes, class-level permissions, purge —
client.schemas(),client.schema(name)withget,create,update,delete,purge; untyped; need the master key
- One-to-one and one-to-many —
Pointer<T> - Many-to-many —
Relation<T>, an array of pointers, or a join class
- Every failure is a sealed
ParseException:ObjectNotFound,DuplicateValue,ScriptFailed,UsernameTaken,EmailTaken,InvalidCredentials,InvalidSession,Unauthorized,NetworkError,DecodeError,OtherParseErrorwith the code - Values the server would store wrongly are rejected before sending —
ArgumentError
- Access control lists —
Acl: public, user and role read and write
Class-level permissions, pointer permissions, cloud-code validation and rate limits are server configuration; the SDK reports their errors.
Storage, the data browser, import and export are server and dashboard features.
The integration tests need Docker: Compose starts parse-server 9.9.0 on MongoDB (port 1337) and on Postgres (port 1338).
tool/ci.shruns every check: dependency and code rules, generated code and fixtures, analyze, format, unit tests (VM, and Chrome for the platform-dependent ones), generator tests, then the integration and live-query suites. PLAN.md records the design, the rules and every server behavior the SDK relies on.
Apache 2.0.