npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

parse-sdk-ts

v1.2.1

Published

A client/server library for typescript that provides type-safety for database classes, queries and cloud functions.

Downloads

707

Readme

Parse Typescript SDK

A client/server library for typescript that provides type-safety for database classes, queries and cloud functions.

Built on top of Parse-SDK-JS, which is a version agnostic peer dependency.

Tested in SSR environment on Next and Nuxt. 🧪

Package info

npm version License

Justification

The Parse-SDK-JS API, is not only prone to spelling errors but is also exceedingly difficult to utilize for developers who are unfamiliar with the names of database keys.

book.set("author", "Tolkien") // 😰 anything accepted
// is now
book.author.set("Tolkien") // 🚀 only string accepted

And more importantly:

book.get("???") // 😰
// is now
book.author.get() // 🚀 full code completion

Please see example/ folder for complete examples with imports.

Setup

npm install parse-sdk-ts

import { initialize }  from 'parse-sdk-ts'

initialize(server_url, app_id)

Auth

import * as auth from 'parse-sdk-ts/auth'
const user : MyUser = await auth.signIn(MyUser, username, password)

Objects

Data is accessed via attributes instead of via string keys.

console.log(user.username.get())
user.username.set("new name")    // string
user.level.increment()           // number
user.weapons.addUnique("sword")  // array 
user.save()

Queries

The API is essentially the same as the Parse-SDK-JS but with Keys instead of strings.

const users : MyUser[] = new Query(MyUser).ascending(MyUser.keys.username).find()

Classes

To provide typings, all classes on the database must be wrapped with some logic.

export class MyUser extends DbModel {
  static readonly className: string = "_User";

  static readonly keys = Keys.build(MyUser, {
    username: "username",
  });

  readonly username = field(this).required().string(MyUser.keys.username);

  // to make sure only Parse.User can be passed
  constructor(data: Primitive.User) {
    super(data);
  }
}

For a non-user object it's the same

import User from './User'

export class Book extends DbModel {
  static readonly className: string = "Book";

  // client_key: "db_key", //
  static readonly keys = Keys.build(Book, {
    title: "book_title",
    authors: "authors",
    description: "desc",
  });

  readonly title       = field(this).required().string(Book.keys.title);
  readonly authors     = field(this).relation(User, Book.keys.authors);
  readonly description = field(this).string(Book.keys.description);
}

To import the field function:

import { field } from "parse-sdk-ts"
// or just use the quickhand
readonly title = this.field().string(Book.keys.title);

To be able to create a new object without data you may want to add the following static method.

static create() {
  return DbModel.createWithoutData(Book);
}

Or to ensure that Required fields can't be undefined.

static create(title: string, description: string) {
  const b = DbModel.createWithoutData(Book);
  b.title.set(title);
  b.description.set(description);
  return b;
}

Fields

We differentiate between Required and Optional fields.

If we are certain that the field is defined in the database, for example user.username.get(), we can use a Required field so that we get get(): string instead of get(): string | undefined.

Even Required fields can be undefined if the object has not been fetched, or if we fetch it via a query with exclude or select. Therefore we can also specify an fallback value that will be returned if the Required field is undefined.

field(this).required(fallback).string(User.keys.username)

Required Fields

| Type | Name |Methods| | ------------- | ------------- |------------- | | String | .required().string |get set| | Number | .required().number |get set increment decrement | | Boolean | .required().boolean |get set| | Date | .required().date |get set| | Array<T> | .required().array<T> |get set append addUnique remove|

Optional Fields

| Type | Name | Methods| | ------------- | ------------- |------------- | | String \| undefined | .string | get set has getOrElse | | Number \| undefined | .number | get set has getOrElse increment decrement | | Boolean \| undefined | .boolean |get set has getOrElse| | Date \| undefined | .date |get set has getOrElse| | Array<T> \| undefined | .array<T> |get set has getOrElse append addUnique remove |

Special Fields

T denotes the type of the target DbModel class. .required() does not have an effect on these fields.

| Name |Methods| Note| | ------------- | ------------- |------------- | | .pointer<T> |get set| A reference to a single other object. | | .stringPointer<T> |get set| Same as Pointer but expects DB field to be a string (uuid) instead of Parse-Pointer. | | .relation<T> |add remove, query, findAll | A reference to a group of other objects. | | .syntheticRelation<T> | query, findAll | Synthesizes a relation like field from the fact that the target class has a pointer to this object. |

Fallback

If you need to run some code that is not supported by the API, you can access the Parse Object directly.

user.data.increment("reactions." + reactionType)

Cloud Functions

We can also declare typed cloud functions in the following way.

import { Cloud } from "parse-sdk-ts";

export const myFunction = 
Cloud.declare<(arg1: string, arg2: number) => number>(
    "my_function",
    ["arg1", "arg2"] // db expected argument names
)

const result:number = await myFunction("hello", 42)

It is even supported to pass our custom DbModel classes.

import { Cloud, Primitive } from "parse-sdk-ts";

const getAuthor = Cloud.declare<(book: Book) => Primitive.User>(
    "get_author",
    ["book"]
)
const author: User = new User(await getAuthor(book))

Note that it is NOT supported to return custom DbModel classes. Therefore we have to wrap the result ourselves.

Server side rendering

Parse-SDK-TS is designed to work with server side rendering. It will automatically detect if it is running on the server. Note that it will never use the parse/node library but instead use the browser version with mocked localstorage. I think that this works just as fine, though it also means that the server will have access to auth functions which it should not use.

You should set the following environment variable to ignore the warning about this.

// .env
SERVER_RENDERING=true

If the master key is provided, Parse-SDK-TS will automatically load it when using the library on the server during initialization.

// .env.local
READONLY_MASTERKEY=...
// or
MASTERKEY=... (priority)

We use the masterkey by doing the following.

user.save({ useMasterKey: true })
query.find({ useMasterKey: true })
// or
query.asMaster().find()
user.saveAsMaster()

Error meta data

Errors are automatically split on ;;, the left side is put into message and the right side JSON parsed and put into meta.

The reason for this is that I wanted to display one message directly to the user and still get some more information to handle on the client.

type DbError = {
  code: number,
  message: string,
  meta: {}
}

For example

{
  message: 'Objektet kunde inte hittas.',
  code: 101,
  meta: { original: 'Object not found.' }
}

or (with zod)

{
    message: 'One field could not be saved.',
    code: 9001,
    meta: {
      key: 'username',
      code: 'too_big',
      maximum: 15,
      type: 'string',
      message: 'Username may not be longer than 15 characters'
    }
  }

Other

Parse is exported as Primitive from parse-sdk-ts. So Parse.Object is now Primitive.Object. We should never need to use it though.

Other exports are: isServer, useLocalTestServer