File I/O
There is no direct access to the filesystem in Val Town. This allows us to deploy and scale your code quickly and efficiently. If you need to read files from within a val, you can via the Val Town API.
Using the SDK
Read a file
import ValTown from "npm:@valtown/sdk";
const vt = new ValTown();
const val_id = "your-val-id-here";
const response = await vt.vals.files.getContent(val_id, {
path: "path/to/file.txt",
});
const content = await response.text();
console.log(content);Create a file
import ValTown from "npm:@valtown/sdk";
const vt = new ValTown();
const val_id = "your-val-id-here";
const file = await vt.vals.files.create(val_id, {
path: "path",
type: "directory",
});Update a file
import ValTown from "npm:@valtown/sdk";
const vt = new ValTown();
const val_id = "your-val-id-here";
const file = await vt.vals.files.update(val_id, {
path: "path/to/file.txt",
content: "New file content",
});List files
import ValTown from "npm:@valtown/sdk";
const vt = new ValTown();
const val_id = "your-val-id-here";
const files = await Array.fromAsync(
vt.vals.files.retrieve(id, {
path: "",
recursive: true,
}),
);Using val utils
Val Town's standard library ships with a suite of utility functions like
readFile and serveFile.
- std/utils — readFile, listFiles, listFilesByPath, httpEndpoint, emailAddress, fetchTranspiledJavaScript, serveFile, getContentType
readFile
Reads the contents of a file from the current val.
Note: you can also import text files, so
readFilemay be unnecessary and add a round trip.
Signature:
readFile(path: string, metaImportUrl?: string): Promise<string>Parameters:
path- The file path to read (with or without leading slash)metaImportUrl- Optional. Theimport.meta.urlto determine which val to read from. Defaults toDeno.env.get("VALTOWN_ENTRYPOINT")
Example:
import { readFile } from "https://esm.town/v/std/utils/index.ts";
// Most common usage - reads from current val
const content = await readFile("/README.md");
console.log(content);
// Explicit val specification
const content2 = await readFile("/README.md", import.meta.url);
console.log(content2);listFiles
Lists all files in the current val with their metadata.
Signature:
listFiles(metaImportUrl?: string): Promise<File[]>Parameters:
metaImportUrl- Optional. Theimport.meta.urlto determine which val to list files from. Defaults toDeno.env.get("VALTOWN_ENTRYPOINT")
Example:
import { listFiles } from "https://esm.town/v/std/utils/index.ts";
// Most common usage - lists files from current val
const files = await listFiles();
files.forEach((file) => console.log(file.path));
// Explicit val specification
const files2 = await listFiles(import.meta.url);
files2.forEach((file) => console.log(file.path));listFilesByPath
Lists files in a specific directory path within the current val.
Signature:
listFilesByPath(path: string, metaImportUrl?: string): Promise<File[]>Parameters:
path- The directory path to list files frommetaImportUrl- Optional. Theimport.meta.urlto determine which val to list files from. Defaults toDeno.env.get("VALTOWN_ENTRYPOINT")
Example:
import { listFilesByPath } from "https://esm.town/v/std/utils/index.ts";
// List files in a specific directory
const frontendFiles = await listFilesByPath("frontend/");
frontendFiles.forEach((file) => console.log(file.path));httpEndpoint
Gets the HTTP endpoint URL for a specific file in the val.
Signature:
httpEndpoint(path: string, metaImportUrl?: string): Promise<string>Parameters:
path- The file path to get the endpoint formetaImportUrl- Optional. Theimport.meta.urlto determine which val the file belongs to. Defaults toDeno.env.get("VALTOWN_ENTRYPOINT")
Example:
import { httpEndpoint } from "https://esm.town/v/std/utils/index.ts";
// Get the HTTP endpoint for a specific file
const endpoint = await httpEndpoint("/api/users.ts");
console.log(endpoint); // Returns the full HTTP URL for the endpointemailAddress
Gets the email address for a file with an email trigger in the val.
Note This function only works for the uuid version and not vanity email addresses because that info isn't in our API yet. Open a GitHub feature request if you'd like us to prioritize this.
Signature:
emailAddress(path: string, metaImportUrl?: string): Promise<string>Parameters:
path- The file path to get the email address formetaImportUrl- Optional. Theimport.meta.urlto determine which val the file belongs to. Defaults toDeno.env.get("VALTOWN_ENTRYPOINT")
Example:
import { emailAddress } from "https://esm.town/v/std/utils/index.ts";
// Get the email address for a file with email trigger
const email = await emailAddress("/handlers/processEmail.ts");
console.log(email); // Returns: username-fileid@valtown.emailNotes:
- The file must have an email trigger configured
- Returns email in format:
{username}-{fileIdWithoutDashes}@valtown.email - Throws an error if the file doesn't exist or doesn't have an email trigger
fetchTranspiledJavaScript
Fetches and transpiles TypeScript/JavaScript code from esm.town URLs.
Signature:
fetchTranspiledJavaScript(url: string): Promise<string>Example:
import { fetchTranspiledJavaScript } from "https://esm.town/v/std/utils/index.ts";
const code = await fetchTranspiledJavaScript(
"https://esm.town/v/std/utils/index.ts",
);
console.log(code);Notes:
- Paths can be specified with or without leading slashes
- TypeScript files are automatically transpiled to JavaScript
- Most functions now have optional
metaImportUrlparameters that default toDeno.env.get("VALTOWN_ENTRYPOINT"), making them easier to use in most cases
serveFile
Serves a file from the val as an HTTP Response with the correct Content-Type header.
Signature:
serveFile(path: string, metaImportUrl?: string): Promise<Response>Parameters:
path- The file path to servemetaImportUrl- Optional. Theimport.meta.urlto determine which val to serve from. Defaults toDeno.env.get("VALTOWN_ENTRYPOINT")
Example:
import { serveFile } from "https://esm.town/v/std/utils/index.ts";
// In a Hono app - most common usage
app.get("/assets/*", async (c) => {
return await serveFile(c.req.path);
});
// Explicit val specification
app.get("/assets/*", async (c) => {
return await serveFile(c.req.path, import.meta.url);
});getContentType
Determines the appropriate MIME type for a file based on its extension.
Signature:
getContentType(path: string): stringExample:
import { getContentType } from "https://esm.town/v/std/utils/index.ts";
console.log(getContentType("index.html")); // "text/html; charset=utf-8"
console.log(getContentType("script.ts")); // "text/javascript"
console.log(getContentType("data.json")); // "application/json; charset=utf-8"Features:
- TypeScript/TSX/JSX files are served as
text/javascript
parseVal
Parses Val Town metadata from an import.meta.url.
Signature:
parseVal(importMetaUrl: string): ValMetadataExample:
import { parseVal } from "https://esm.town/v/std/utils/index.ts";
const val = parseVal(import.meta.url);
console.log(val);
// Output: { owner: "username", name: "valname", ... }Prior Art:
staticHTTPServer
Creates a Hono-based HTTP server that serves static files from your val. The
server automatically serves /index.html for root requests and handles all
other file paths.
Signature:
staticHTTPServer(importMetaURL?: string): (request: Request) => Promise<Response>Parameters:
importMetaURL- Optional. Theimport.meta.urlto determine which val to serve files from. Defaults toDeno.env.get("VALTOWN_ENTRYPOINT")
Example:
import { staticHTTPServer } from "https://esm.town/v/std/utils/index.ts";
// Most common usage - serves files from current val
export default staticHTTPServer();
// Explicit val specification
export default staticHTTPServer(import.meta.url);Features:
- Automatically serves
/index.htmlfor root path requests (/) - Handles all file paths with proper content types
- Built on Hono for reliable HTTP handling
- Includes error unwrapping for better debugging
- Returns a fetch-compatible handler function
Use Cases:
- Single-page applications (SPAs)
- Static websites
- Documentation sites
- Asset serving for web applications
Notes:
- The server will attempt to serve
/index.htmlfor root requests - All other paths are served as-is from the val's file system
- Non-existent files will throw "Failed to fetch file" errors
- The returned function is compatible with Val Town's HTTP trigger system
isMain
Checks if the current file is the main entry point of the val. This is a
replacement for import.meta.main which is not available in Val Town's
environment.
Signature:
isMain(importMetaURL: string): booleanParameters:
importMetaURL- Theimport.meta.urlof the current file
Example:
import { isMain } from "https://esm.town/v/std/utils/index.ts";
if (isMain(import.meta.url)) {
console.log("This file is being run directly");
// Run main logic here
} else {
console.log("This file is being imported");
// Export functions/classes for use by other files
}Use Cases:
- Conditional execution of code only when file is run directly
- Separating library code from executable code in the same file
- Testing and development workflows
- Creating dual-purpose files that can be both imported and executed
Notes:
- This function compares the provided
import.meta.urlwith theVALTOWN_ENTRYPOINTenvironment variable - In Val Town, the entry point is determined by which file is being executed directly (e.g., via HTTP trigger, cron, or manual execution)
- This is equivalent to Python's
if __name__ == "__main__":pattern or Node.js'srequire.main === module - Unlike Deno's
import.meta.main, this works reliably in Val Town's serverless environment