TypeScript configuration
Write your deployment configuration as gigadrive.ts, with editor type checking and values your own code computes.
gigadrive.ts holds the same configuration gigadrive.yaml does, written as a TypeScript module the build executes. Your editor checks the keys as you type, and the file can build parts of the configuration from your own code instead of listing them by hand.
Set it up
Install the config package
The package supplies the types. Keep it as a dev dependency.
npm install --save-dev @gigadrive/network-configWrite gigadrive.ts at the project root
Export the configuration object as the default export, and type it with
ConfigV4.import type { ConfigV4 } from '@gigadrive/network-config'; export default { version: 4, assets: 'public', functions: { 'api/report.js': { runtime: 'node-22', memory: 1024, max_duration: 120, }, }, routes: [ { source: '^/api/report(/.*)?$', destination: 'api/report.js', methods: ['GET', 'POST'], }, ], } satisfies ConfigV4;Delete the file it replaces
Keep one configuration file.
gigadrive.tsis found beforegigadrive.yaml, so leaving both in the repository means the YAML is silently ignored and nothing says so.gigadrive debug configprints the path that answered.
version: 4 is still required and is still a number. Every other key is the one Configuration reference documents, spelled the same way, including the snake_case names such as max_duration and build_commands.
Type checking and autocomplete
import type is the form to reach for. TypeScript erases it before the file runs, so ConfigV4 costs the deployment nothing and satisfies still gives you autocomplete, a red squiggle on a misspelled key, and a rejection of a memory outside 128 to 3009 or a runtime that does not exist.
The package also exports defineConfig, an identity function that types its argument the same way:
import { defineConfig } from '@gigadrive/network-config/define-config';
export default defineConfig({
version: 4,
assets: 'dist',
});Import it from the @gigadrive/network-config/define-config subpath rather than the package root. The subpath ships its own type declarations and imports nothing else, while the package root pulls in effect and @effect/platform as peer dependencies that not every package manager installs for you.
defineConfig is a value import, so a deployment has to resolve it
Unlike import type, defineConfig is still there when the file runs, and the first read of your configuration
happens before the install step. Use it locally, where gigadrive debug config and gigadrive deploy run against an
installed node_modules, and prefer satisfies ConfigV4 in a file you deploy. Both produce the same configuration.
Computing the configuration
The file is executed, so a value can come from your own code rather than from a literal. This is the reason to pick TypeScript over YAML: a project whose entrypoints or routes are already described somewhere in the repository can derive them instead of repeating them.
import type { ConfigV4 } from '@gigadrive/network-config';
import { endpoints } from './src/endpoints';
export default {
version: 4,
assets: 'public',
functions: Object.fromEntries(endpoints.map((endpoint) => [endpoint.file, { runtime: 'node-22', memory: 512 }])),
routes: endpoints.map((endpoint) => ({
source: endpoint.path,
destination: endpoint.file,
})),
} satisfies ConfigV4;Relative imports resolve from the config file's own directory, so ./src/endpoints works from a gigadrive.ts beside it. Everything at the top level of the file runs when the deployment reads the configuration, on the same trust footing as a build command.
Two inputs are not available to that code, and a file that depends on either behaves differently on your machine than it does on a deployment:
- Environment variables. The configuration is read outside the environment your build runs in, so
process.envholds none of the variables you set for the application. Environment variables covers what does reach a build and a Function. - Installed packages. A deployment reads the configuration from the fetched source, before the install step, and reads it again after your build. An import that resolves inside your repository works at both points. An import of a package from
node_modulesresolves only at the second one.
The other extensions
The same module handling applies to five more filenames, so a project that is not written in TypeScript still gets the executable form:
| Filename | Export with |
|---|---|
gigadrive.ts, gigadrive.mts, gigadrive.mjs | export default |
gigadrive.js, gigadrive.cjs, gigadrive.cts | module.exports =, or export default in an ESM project |
.cjs and .cts are CommonJS by definition, so they are read through the interop path and module.exports is what they export. The three ESM-only extensions need a real default export, and a file carrying only named exports is reported as having none.
// gigadrive.cjs
module.exports = {
version: 4,
assets: 'dist',
};Configuration lists every accepted filename in the order the build checks them.
What the export has to be
The default export has to be the configuration object itself. A function is rejected on purpose, so there is no hook for deciding the configuration at the moment the deployment reads it. Arrays, strings and numbers are rejected too.
| Message | Cause |
|---|---|
Config file is empty at ... | The file has no contents. Checked before it is evaluated |
Failed to load config module at ... | A syntax error, an import that does not resolve, or a top-level statement that threw |
... does not have a default export | A .ts, .mts or .mjs file with only named exports |
... config factories are not supported | The default export is a function |
... must default-export a config object | The default export is a string, a number or an array |
Config file is missing version at ... | The exported object has no version, or its version is not a number |
A file that loads and exports an object goes through the same schema validation as YAML, so a bad value inside it fails the build with the path to that value.
Migrating from gigadrive.yaml
Translate the YAML into an object literal, keeping every key name as it was, and delete the YAML file:
version: 4
assets: public
functions:
api/report.js:
runtime: node-22
memory: 1024import type { ConfigV4 } from '@gigadrive/network-config';
export default {
version: 4,
assets: 'public',
functions: {
'api/report.js': {
runtime: 'node-22',
memory: 1024,
},
},
} satisfies ConfigV4;Run gigadrive debug config afterwards. It resolves the configuration through the same code path a deploy uses and prints the result, so you can compare it against what the YAML produced before you deploy. Debugging configuration covers the output.
