Skip to main content
Version: Next

Frontend client

Create implementation and type files that exposes a client for a remote OpenAPI server, that uses fetch and can run in any browser.

Generating the Client

To create a client for a remote OpenAPI API, use the following command:

$ platformatic client http://example.com/to/schema/file --frontend --language <language> --name <clientname>
  • <language>: Can be either js (JavaScript) or ts (TypeScript).
  • <clientname>: The name of the generated client files. Defaults to api.

This command creates two files: clientname.js (or clientname.ts) and clientname-types.d.ts for TypeScript types.

Usage

The generally implementation exports named operations and a factory object.

Named operations

import { setBaseUrl, getMovies } from './api.js'

setBaseUrl('http://my-server-url.com') // modifies the global `baseUrl` variable

const movies = await getMovies({})
console.log(movies)

Factory

The factory object is called build and can be used as follows:

import build from './api.js'

const client = build('http://my-server-url.com')

const movies = await client.getMovies({})
console.log(movies)

You can use both named operations and the factory in the same file. They can work on different hosts, so the factory does not use the global setBaseUrl function.

Generated Code

TypeScript Types

The type file will look like this:

export interface GetMoviesRequest {
'limit'?: number;
'offset'?: number;
// ... all other options
}

interface GetMoviesResponseOK {
'id': number;
'title': string;
}
export interface Api {
setBaseUrl(newUrl: string) : void;
getMovies(req: GetMoviesRequest): Promise<Array<GetMoviesResponseOK>>;
// ... all operations listed here
}

type PlatformaticFrontendClient = Omit<Api, 'setBaseUrl'>
export default function build(url: string): PlatformaticFrontendClient

JavaScript Implementation

The javascript implementation will look like this

let baseUrl = ''
/** @type {import('./api-types.d.ts').Api['setBaseUrl']} */
export const setBaseUrl = (newUrl) => { baseUrl = newUrl }

/** @type {import('./api-types.d.ts').Api['getMovies']} */
export const getMovies = async (request) => {
return await _getMovies(baseUrl, request)
}
async function _createMovie (url, request) {
const response = await fetch(`${url}/movies/`, {
method:'post',
body: JSON.stringify(request),
headers: {
'Content-Type': 'application/json'
}
})

if (!response.ok) {
throw new Error(await response.text())
}

return await response.json()
}

/** @type {import('./api-types.d.ts').Api['createMovie']} */
export const createMovie = async (request) => {
return await _createMovie(baseUrl, request)
}
// ...

export default function build (url) {
return {
getMovies: _getMovies.bind(url, ...arguments),
// ...
}
}

TypeScript Implementation

The typescript implementation will look like this:

import type { Api } from './api-types'
import type * as Types from './api-types'

let baseUrl = ''
export const setBaseUrl = (newUrl: string) : void => { baseUrl = newUrl }

const _getMovies = async (url: string, request: Types.GetMoviesRequest) => {
const response = await fetch(`${url}/movies/?${new URLSearchParams(Object.entries(request || {})).toString()}`)

if (!response.ok) {
throw new Error(await response.text())
}

return await response.json()
}

export const getMovies: Api['getMovies'] = async (request: Types.GetMoviesRequest) => {
return await _getMovies(baseUrl, request)
}
// ...
export default function build (url) {
return {
getMovies: _getMovies.bind(url, ...arguments),
// ...
}
}