-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Usage feedback & questions #8
Comments
Hi @hmil, thanks for the comments! Here are my thoughts on the following: Path parametersYou're right that TS doesn't support string literal concatenation. In these cases, you do the following:
This will allow you to reap the benefits of type checking with fully dynamic routes. I hope TS adds pattern matching someday so the explicit route declaration can go away. Free form API definitionThis is a good point. In the past it was hard to make a good, helpful "base type" (see No semantic encapsulationThis is outside the scope of RESTyped because it implies the addition of runtime code to support looking up these named "routes". Kinda reminds me of GraphQL. It would be super easy to write your own wrapper around your RESTyped API calls if you want this kind of functionality. Requests payload validationAgain, RESTyped adds no runtime code. You can use |
Hey @rawrmaan , I ended up implementing the ideas above. The result is similar to restyped, but the goal differs in the following points:
You can check out the WIP in this repository and I would love to get your feedback. There are currently some issues when you use it without restyped, but overall it is working. Of course, this module isn't competing with restyped. Rather, it is a different take on a similar problem, given different constraints. Hopefully this effort will help get more people concerned about API type safety in general (you can't say no to everything when you have a wide range of solutions to chose from)! Below is a side-by-side comparison of both frameworks: Restyped: export interface MySocialAPI {
'/users': {
// Route name (without prefix, if you have one)
GET: {
// Any valid HTTP method
query: {
// Query string params (e.g. /me?includeProfilePics=true)
includeProfilePics?: boolean
}
response: User[] // JSON response
}
}
'/user/:id/send-message': {
POST: {
params: {
// Inline route params
id: string
}
body: {
// JSON request body
message: string
}
response: {
// JSON response
success: boolean
}
}
}
}
// Usage with express
const apiRouter = express.Router()
app.use('/api', apiRouter)
const router = RestypedRouter<MySocialAPI>(apiRouter)
router.post('/users/:id/send-message', async req => {
// ...
})
// Usage with axios
const api = axios.create<MySocialAPI>({
baseURL: 'https://fooddelivery.com/api/'
})
const res = await api.post('/user/2/send-message', {
message: 'Hello type sanity!'
}); Rest.ts export const MySocialAPI = defineAPI({
getUsers: GET '/users'
.query({
includeProfilePics: rt.Boolean, // Or just 'false' or 'true' if not using runtypes
})
.response(rt.Array(User))
postMessage: POST `/user/${'id'}/send-message`
.body({
message: rt.String
})
.response({
success: rt.Boolean
})
});
// Usage with express
const router = createRouter(MySocialAPI, {
postMessage: async req => {
// ...
}
});
app.use(router);
// Usage with axios
const api = createConsumer(MySocialAPI, axios.create({
baseURL: 'https://fooddelivery.com/api/'
}));
const res = await api.postMessage({
params: {
id: '2'
},
body: {
message: 'Hello type sanity!'
}
}); Let me know what you think! |
Hi, I like the idea behind this module and I think it is a step in the right direction. Now when using it, I got some frustration which makes me think there is room for improvement. Below are some of the issues I faced, I would like to hear your opinion on these:
Path parameters
There seems to be an issue when a path contains parameters (such as
/users/:id
), the typings become all confused in that case. I believe it is impossible to get proper type definitions for such URLs using your approach because TypeScript doesn't support string literal concatenation.Free form API definition
When defining an API, one has to write a free form interface. There is no safeguard there, no autocompletion (was it "params"? "parameters"? or maybe "query?", you have to check the doc), and no type checking of the definition itself. This makes the experience of writing the API definition a little uncomfortable.
No semantic encapsulation
I understand your approach of defining the types based on the route to keep the layer thin. However, I feel there's a missed opportunity for a better encapsulation. IMO the route is an implementation detail just like the type of the query, response and the query parameters. I see no valid reason to promote the route as the primary identifier of an API endpoint within the TS source.
I mean that instead of writing this:
You could have:
Then, at usage:
Note that the second approach could help solve the issue with path parameters and even help navigating from the client to the server with a "Find all references" from an IDE.
Requests payload validation
This is arguably outside of the scope of this module, but the golden rule of server side development being "Don't trust user input", it is favorable to have some sort of payload validation mechanism.
I haven't put a lot of thought into this yet, but how would Restyped play with existing validation libraries?
You must have gained some valuable insights when working on this module and that is why I would love to hear your opinion on these issues.
The text was updated successfully, but these errors were encountered: