Skip to content

Quickstart

Jumpstart your development with this Quickstart guide. Learn to seamlessly integrate Remult in various stacks, from installation to defining entities for efficient data querying and manipulation.

Try Remult Online

The fastest way to try Remult is in an online REPL.

This minimal example contains a monorepo with a simple Express backend, a vanilla JS client powered by Vite, a few entity classes and a simple script which performs various operations on them from client-side code.

Installation

The remult package is all you need for both frontend and backend code. If you're using one package.json for both frontend and backend (or a meta-framework) - install Remult once in the project's root folder. If you're using multiple package.json files (monorepo) - install Remult in both server and client folders.

sh
npm install remult
sh
yarn add remult
sh
pnpm add remult
sh
bun add remult

Server-side Initialization

Remult is initialized on the server-side as a request handling middleware, with a single line of code. Here is the code for setting up the Remult middleware:

ts
import express from 'express'
import { remultExpress } from 'remult/remult-express'

const app = express()

app.use(remultExpress({})) 

app.listen(3000)
ts
import fastify from 'fastify'
import { remultFastify } from 'remult/remult-fastify'

(async () => {
  const server = fastify()

  await server.register(remultFastify({})) 

  server.listen({ port: 3000 })
})()
ts
// src/app/api/[...remult]/route.ts

import { remultNextApp } from 'remult/remult-next'

export const api = remultNextApp({}) 

export const { GET, POST, PUT, DELETE } = api
ts
// src/routes/api/[...remult]/+server.ts

import { remultSveltekit } from 'remult/remult-sveltekit'

export const _api = remultNextApp({}) 

export const { GET, POST, PUT, DELETE } = _api
ts
// server/api/[...remult].ts

import { remultNuxt } from 'remult/remult-nuxt'
import { Task } from '~/shared/Task.js'

export const api = remultNuxt({
  entities: [Task],
})

export default defineEventHandler(api)
ts
import { type Plugin, server } from '@hapi/hapi'
import { remultHapi } from 'remult/remult-hapi'

(async () => {
  const hapi = server({ port: 3000 })

  await hapi.register(remultHapi({})) 

  hapi.start()
})()
ts
// src/main.ts

import { remultExpress } from 'remult/remult-express'

async function bootstrap() {
  const app = await NestFactory.create(AppModule)

  app.use(remultExpress({})) 

  await app.listen(3000)
}
bootstrap()
ts
import * as koa from 'koa'
import * as bodyParser from 'koa-bodyparser'
import { createRemultServer } from 'remult/server'

const app = new koa()

app.use(bodyParser())

const api = createRemultServer({})

app.use(async (ctx, next) => {
  const r = await api.handle(ctx.request)
  if (r) {
    ctx.response.body = r.data
    ctx.response.status = r.statusCode
  } else return await next()
})

app.listen(3000, () => {})

Connecting a Database

Use the dataProvider property of Remult's server middleware to set up a database connection for Remult.

Recommended - Use default local JSON files and connect a database later

If the dataProvider property is not set, Remult stores data as JSON files under the ./db folder.

Here are examples of connecting to some commonly used back-end databases:

Install node-postgres:

sh
npm i pg

Set the dataProvider property:

ts
import express from "express"
import { remultExpress } from "remult/remult-express"
import { createPostgresDataProvider } from "remult/postgres"

const app = express()

const connectionString = "postgres://user:password@host:5432/database"

app.use(
  remultExpress({
    dataProvider:
      createPostgresDataProvider({
        connectionString, // default: process.env["DATABASE_URL"]
        // configuration: {} // optional = a `pg.PoolConfig` object or "heroku"
      })
  })
)

Or use your existing postgres connection

ts
import { Pool } from 'pg'
import { SqlDatabase } from 'remult'
import { PostgresDataProvider } from 'remult/postgres'
import { remultExpress } from 'remult/remult-express'
const pg = new Pool({
  connectionString: '....',
})
const app = express()
app.use(
  remultExpress({
    dataProvider: new SqlDatabase(new PostgresDataProvider(pg)),
  }),
)

Integrate Auth

Remult is completely unopinionated when it comes to user authentication. You are free to use any kind of authentication mechanism, and only required to provide Remult with a getUser function that extracts a user object (which implements the minimal Remult UserInfo interface) from a request.

Here are examples of integrating some commonly used auth providers:

ts
import express from 'express'
import session from 'express-session'
import { remultExpress } from 'remult/remult-express'

const app = express()

app.use(
  session({
    /* ... */
  }),
)

app.post('/api/signIn', (req, res) => {
  req.session!['user'] = { id: 1, name: 'admin', roles: ['admin'] }
})

app.use(
  remultExpress({
    getUser: (req) => req.session!['user'], 
  }),
)
ts
// src/app/api/[...remult]/route.ts

import { remultNextApp } from 'remult/remult-next'
import { getServerSession } from 'next-auth'
import { authOptions } from '../auth/[...nextauth]/route'

export const api = remultNextApp({
  getUser: async () => {
    const user = (await getServerSession(authOptions))?.user
    return user?.email && user?.name
      ? { id: user?.email, name: user?.name }
      : undefined
  },
})

export const { POST, PUT, DELETE, GET, withRemult } = api

Defining and Serving an Entity

Remult entity classes are shared between frontend and backend code.

ts
// shared/product.ts

import { Entity, Fields } from 'remult'

@Entity('products', {
  allowApiCrud: true,
  allowApiDelete: 'admin',
})
export class Product {
  @Fields.uuid()
  id!: string

  @Fields.string()
  name = ''

  @Fields.number()
  unitPrice = 0
}

Alternatively, generate entities from an existing Postgres database.

Serve Entity CRUD API

All Remult server middleware options contain an entities array. Use it to register your Entity.

ts
// backend/index.ts

app.use(
  remultExpress({
    entities: [Product], 
  }),
)

Using your Entity on the Client

To start querying and mutating data from the client-side using Remult, use the remult.repo function to create a Repository object for your entity class. This approach simplifies data operations, allowing you to interact with your backend with the assurance of type safety.

ts
// frontend/code.ts

import { remult } from 'remult'
import { Product } from '../shared/product'

const productsRepo = remult.repo(Product)

async function playWithRemult() {
  // add a new product to the backend database
  await productsRepo.insert({ name: 'Tofu', unitPrice: 5 })

  // fetch products from backend database
  const products = await productsRepo.find({
    where: { unitPrice: { '>=': 5 } },
    orderBy: { name: 'asc' },
    limit: 10,
  })
  console.log(products)

  // update product data
  const tofu = products.filter((p) => p.name === 'Tofu')
  await productsRepo.save({ ...tofu, unitPrice: tofu.unitPrice + 5 })

  // delete product
  await productsRepo.delete(tofu)
}

playWithRemult()

Client-side Customization

Recommended Defaults

By default, remult uses the browser's fetch API, and makes data API calls using the base URL /api (same-origin).

Changing the default API base URL

To use a different origin or base URL for API calls, set the remult object's apiClient.url property.

ts
remult.apiClient.url = 'http://localhost:3002/api'

Using an alternative HTTP client

Set the remult object's apiClient.httpClient property to customize the HTTP client used by Remult:

ts
import axios from 'axios'
import { remult } from 'remult'

remult.apiClient.httpClient = axios
ts
//...
import { HttpClientModule, HttpClient } from '@angular/common/http'
import { remult } from 'remult'

@NgModule({
  //...
  imports: [
    //...
    HttpClientModule,
  ],
})
export class AppModule {
  constructor(http: HttpClient) {
    remult.apiClient.httpClient = http
  }
}

MIT Licensed | Made by the Remult team with ❤️