Skip to content

minosss/api

Repository files navigation

Hey 👋, @yme/api is a package that defines the type-safe API routes. No server required and zero dependencies.

If you are developing a full-stack web application, you should take a look tRPC.

NPM version NPM Downloads Bundle Size

Why

  • I am a frond-end developer.
  • [module].[action] is good for me. (e.g. post.create(input))
  • I usually use axios but there may be others. (e.g. fetch)
  • It may be need some transformer for each api response. because my colleagues are very unrestrained

So I create this package to solve these problems. (I hope so)

🚧 TODOs

  • with react-query
  • generate from api docs e.g. swagger

Install

pnpm add @yme/api

Quick Start

import { createApi, createRouter } from '@yme/api';
import axios from 'axios';
import { z } from 'zod';

const request = axios.create({}).request;

const router = createRouter();

const createUserSchema = z.object({
  username: z.string().min(1),
  password: z.string().min(1),
  age: z.number().optional(),
});

type CreateUserInput = z.infer<typeof createUserSchema>;

interface UserType {
  id: number;
  username: string;
}

const routes = {
  users: {
    create: router
      .post('/users') // api.user.create({...}) => POST /users
      .validator(createUserSchema) // (input: CreateUserInput) => any
      .T<UserType>()
      .selector((user) => user.id), // (user: UserType) => number
    delete: router
      .delete('/users/:id') // api.user.delete(123) replace path with input => DELETE /users/123
      .validator(z.number())
      .T<void>(), // (input: number) => void
    one: router
      .get('/users/:id') // api.user.one(123)
      .T<UserType>(), // (input: number | {id: number}) => UserType (input type is number | string or {id: number | string} from path params)
    update: router
      .put('/users/:id') // api.user.update(input) will replace with input[id] => PUT /users/{id}
      .validator(
        z.object({
          username: z.string().optional(),
          age: z.number().optional(),
          id: z.number(),
        }),
      )
      .T<UserType>(), // (input: {username?: string; age?: number; id: number}) => UserType
    list: router
      .get('/users') // api.user.list({page: 1}) => GET /users?page=1
      .T<{ page: number }, { list: UserType[] }>()
      .validator(({ page }) => (page > 0 ? { page } : { page: 1 })) // (input: {page: number}) => {page: number}
      .selector(({ list }) => list), // (listResult: {list: UserType[]}) => UserType[]
  },
};

const api = createApi({
  http: request,
  routes,
});

declare module '@yme/api' {
  interface Register {
    api: typeof api;
  }
}

// use api
const userId = await api.users.create({ username: 'yoyo', password: 'yoyo123' });
console.log(`user id: ${userId}`);

// delete user
await api.users.delete(userId);
// done.

How to define a route?

// method and path always required
router.post('/users');

// method, path and validator (input)
router.post('/users').validator(schema);

// with selector
router.post('/users').T<UserType>().selector((user) => user.id);
router.post('/users').selector((user: UserType) => user.id);

// method, path, validator (input) and selector (output)
router.post('/users').validator(schema).selector((user: UserType) => user.id);

// with types (input, output)
router.post('/users').T<In, Out>();

// validator and output
router.post('/users').validator(schema).T<Out>();

// types first
router.post('/users').T<In, Out>().validator((in) => in).selector(out => out);

Create a api resource (v1.2.0)

const r = createResource('/users');

const user = {
  // GET /users
  list: r.get().T<{records: UserType[]; total: number}>(),
  // GET /users/:id
  one: r.get('/:id'),
  // POST /users
  create: r.post().validator(userSchema).selector((user: UserType) => user.id),
  // PUT /users/:id (/:id by defaults)
  update: r.put().validator(z.object({name: z.string(); id: z.string()})).T<UserType>(),
  // DELETE /users/:id
  delete: r.delete(),
  // POST /users/:id/reset-password
  resetPassword: r.post('/:id/reset-password').validator(z.object({password: z.string()})).T<boolean>(),
}

const api = createApi({routes: { user }});

License

MIT

About

define type-safe API routes simply

Resources

License

Stars

Watchers

Forks

Packages

No packages published