| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
If you're developing a client & server app both in TypeScript, you can leverage TypePoint to DRYly define your RESTful API endpoints to have strongly typed requests and responses, without duplicating or generating code.
First we need to install the following packages:
npm add @typepoint/client @typepoint/server @typepoint/shared @typepoint/expressWe start by defining an endpoint in a shared folder. Our example will define an endpoint to return a Todo with a given id passed as a path param.
import { defineEndpoint, Empty } from '@typepoint/shared';
export interface Todo {
id: string;
title: string;
isCompleted: boolean;
}
export interface GetTodoRequestParams {
id: string;
}
export const getTodoEndpoint = defineEndpoint<GetTodoRequestParams, Empty, Todo[]>((path) =>
path.literal('/api/todos/').param('id'),
);Our getTodoEndpoint variable defines our endpoint, including:
Next, let's create a handler for our endpoint in our server.
import { createHandler } from '@typepoint/server';
import { getTodoEndpoint } from '../../shared/endpoints/getTodoHandler';
import { getTodoById } from './todoService';
export const getTodoHandler = createHandler(getTodoEndpoint, async ({ request, response }) => {
// Get todo from our async todo service and put it in our response body
response.body = await getTodoById(request.params.id);
});Next we need to create a router in order to route requests to our handlers.
import { Router } from '@typepoint/server';
import { getTodoHandler } from './todos/getTodoHandler';
export const router = new Router({
// We just have one handler for now
handlers: [getTodoHandler],
});Finally, we need to connect our router to our web server.
import express = require('express');
import * as bodyParser from 'body-parser';
import { toMiddleware } from '@typepoint/express';
import { router } from './router';
async function run() {
const app = express();
const port = 3001;
app.use(bodyParser.json());
const handlerMiddleware = toMiddleware(router);
app.use(handlerMiddleware);
app.listen(port, () => {
console.log(`API Server running at http://localhost:${port}`);
});
}
run().catch(console.error);Now if we run ts-node ./server we'll have a working server that will handle getting a todo.
On the front-end, we need to create a Client in order to make requests.
import { TypePointClient } from '@typepoint/client';
export const client = new Client({
server: 'http://localhost:3001',
});Now we have our TypePointClient created, we can use it anywhere in our front-end.
import { client } from './typepoint';
import { getTodoEndpoint } from '../shared/endpoints/getTodoEndpoint';
async function showTodo() {
const response = await client.fetch(getTodoEndpoint, {
params: {
id: '1',
},
});
const todo = response.body;
alert(todo.title);
}The above client side code is completely typed. Trying to fetch from the getTodoEndpoint without passing the a string id as a param will cause a design/compile-time error. The response is also completely typed.
The @typepoint/react library provides react hooks to call your endpoints.
npm add @typepoint/reactBefore you can start using TypePoint React hooks, you'll need to wrap your root component with a TypePointProvider. This provides the client that the hooks will use.
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { TypePointClient } from '@typepoint/client';
import { TypePointProvider } from '@typepoint/react';
import { App } from './app';
const client = new TypePointClient({
// Path to API server, normally this will be an environment variable
server: 'http://localhost:3001',
});
export const AppWithProviders = React.memo(() => (
<TypePointProvider client={client}>
<App />
</TypePointProvider>
));
const root = window.document.getElementById('root');
ReactDOM.render(<AppWithProviders />, root);useEndpoint is a hook that immediately fetches a given endpoint with the given params and or body.
import React from 'react';
import { useEndpoint } from '@typepoint/react';
import { getTodosEndpoint } from '../shared/endpoints/getTodosEndpoint';
const TodoApp = () => {
const { response } = useEndpoint(getTodosEndpoint, {});
const todos = response?.body ?? [];
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
};
export default TodoApp;The endpoint will not be fetched unnecessarily, only when the params change. In the above example no params are required so it will only be fetched once.
useEndpoint also returns a refetch function which can be called imperatively to refetch the endpoint with the last used params and body. This is useful for refreshing data.
const { response, refetch } = useEndpoint(getTodosEndpoint, {});useEndpoint also returns the loading state of the request, as well as an error if there is one.
const TodoApp = () => {
const { response } = useEndpoint(getTodosEndpoint, {});
const { response, loading, error } = useEndpoint(getTodosEndpoint, {});
if (loading) {
return <Spinner />;
}
if (error) {
return <div>Something went wrong!</div>;
}
const todos = response?.body ?? [];
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
};useEndpointLazily is just like useEndpoint except that it does not immediately fetch the endpoint, instead it provides a fetch function to let you fetch the given endpoint on demand.
import React from 'react';
import { useEndpointLazily } from '@typepoint/react';
import { addTodoEndpoint } from '../shared/endpoints/addTodoEndpoint';
/**
* Component which can create a new todo.
*/
const NewTodoInput = () => {
const [title, setTitle] = useState('');
const { fetch: addTodo } = useEndpointLazily(addTodoEndpoint);
const addTodoWithTitle = useCallback((title: string) => {
addTodo({ body: { title } });
}, [addTodo]);
return (
<div>
<input type="text" value=>
<button type="button" onClick={addTodoWithTitle}>Add</button>
</div>
);
};
export default TodoApp;Check out the examples repository for example apps that use TypePoint.
Got a problem or suggestion? Submit an issue!
Want to contribute? Fork the repository and submit a pull request! 🌩
| Back | FazBrowse Home | New Git URL |