Meet tiny-plates: a JavaScript SDK and CLI for the recipe API
By Chris Artmann ·
Until now, talking to Tiny Plates from code meant writing your own fetch calls. That works. But you end up building the same little wrapper every time. You add the auth header. You turn ingredients into a comma-separated string. You check the status code and guess what went wrong.
So we built the wrapper for you. Say hi to tiny-plates, our JavaScript and TypeScript SDK. It comes with a command line tool too.
npm install tiny-plates
Here's your first request:
import { createClient } from 'tiny-plates'
const tinyPlates = createClient({
apiKey: process.env.TINY_PLATES_API_KEY ?? ''
})
const { results } = await tinyPlates.search({ query: 'carbonara', limit: 5 })
for (const result of results) {
console.log(result.title)
}
That's it. Every endpoint has a typed method, and every response type is exported. Your editor knows what a recipe looks like, so you don't have to keep the docs open in another tab.
It also has no runtime dependencies. It runs in Node 20 or later, in Bun, and anywhere else with fetch. It works with both import and require.
A few things it does for you
Lists are real lists. You pass ingredients: ['egg', 'tomato'], and the client takes care of the query string. Here's how to find what you can cook tonight, missing at most one ingredient and with no milk:
const { recipes } = await tinyPlates.pantry.list({
ingredients: ['egg', 'tomato', 'onion'],
maxMissing: 1,
without: ['milk']
})
Scaling a recipe is one call. So is a shopping list for the whole week:
const { recipe } = await tinyPlates.recipes.byId('carbonara', {
servings: 4,
units: 'metric'
})
const list = await tinyPlates.shoppingList.build([
{ id: 'carbonara', servings: 4 },
{ id: 'caesar-salad' }
])
Paging is handled too. Popular, pantry and filtered recipe lists all have an iterate method. It walks every page for you, and you just loop:
for await (const recipe of tinyPlates.pantry.iterate({
ingredients: ['egg', 'tomato'],
maxMissing: 1
})) {
console.log(recipe.title, recipe.coverage)
}
Errors are friendly, too. Every error is a class you can check with instanceof, like NotFoundError or RateLimitError. A rate limit tells you how many seconds are left in retryAfter. The messages say what went wrong and what to do about it, so you can show them to your users as they are. Connection hiccups and server errors get retried twice before you ever see them.
If you build with Effect, there's a client for you as well. Import from tiny-plates/effect. You get typed errors per endpoint and responses checked against the schema.
Recipes in your terminal
The same package gives you a tiny-plates command. You don't need to install anything. Just run it with npx:
$ npx tiny-plates search "Veggie loaded lunch"
1. Loaded Turkey Panini
American · Lunch · 10 min · Serves 1 · ★ 4.8 (4)
69e220e4d0295ac8a9011873
2. Loaded Baked Sweet Potato "Healthified"
American · Dinner · 1 h 5 min · Serves 4 · ★ 4.9 (37)
69e230ccd0295ac8a90120c5
3. Roasted Vegetable Bowls with Green Tahini
Lunch · 45 min · Serves 6 · ★ 4.9 (54)
69e2212bd0295ac8a901189b
Run tiny-plates recipe <id> to see a recipe.
You don't need to copy an API key either. Run tiny-plates login and it opens Tiny Plates in your browser. Sign in once and you're set. In scripts and CI, set TINY_PLATES_API_KEY instead.
The CLI can do everything the API can. Here are a few favourites:
tiny-plates recipe carbonara --servings 4 --units metric
tiny-plates pantry egg tomato onion --max-missing 1 --without milk
tiny-plates shopping-list carbonara:4 caesar-salad
tiny-plates substitutions buttermilk
We use it all the time to poke at the data before writing any code. It's handy for checking what a search really returns. Add --json to any command to get the raw response, and pipe it into jq:
tiny-plates search carbonara --json | jq '.results[].title'
Give it a try. You can sign up for a free key with 50 requests a day, or just run npx tiny-plates login. The JavaScript client docs and the command line docs have the full tour. The package is on npm, and the source is on GitHub under the MIT license. If something feels off, open an issue. We'd love to hear what you build.