Software Engineering

Learning Systems Through Search: Building PgSeek with PostgreSQL, TypeScript, Docker, and Containers

Published May 12, 2026 PostgreSQL Docker

PgSeek started as a searchable file and document application. It became a practical way to learn how real systems connect: a Nuxt 4 and Vue frontend, TypeScript server routes, PostgreSQL-backed storage, Dockerized infrastructure, and a repeatable container workflow.

PgSeek hero image representing PostgreSQL, Docker containers, and TypeScript development

The gap between using tools and understanding systems

There is a major difference between using a database, running a framework, and actually understanding how a full-stack system behaves when those pieces have to work together. PgSeek was built to make that gap much smaller.

The project looks simple from the outside: upload files, store them in PostgreSQL, search them, preview safe content, and delete records when they are no longer needed. But underneath that workflow are the same engineering concerns that show up in production systems: storage, indexing, API design, type safety, container networking, migrations, environment variables, and repeatable environment setup.

That is what made PgSeek valuable. It was not just a feature project. It was a learning project built around real infrastructure.

Why search was the right problem to build around

Search is easy to underestimate. A user types a query, receives results, and expects the experience to feel instant. Behind that simple interaction is a chain of decisions about data structure, ranking, filtering, indexing, and how much work the database should do.

PgSeek uses PostgreSQL as more than a place to store rows. Uploaded files are stored, listed, previewed, and deleted through PostgreSQL-backed API routes, while the repository also includes a document search endpoint that experiments with PostgreSQL full-text search.

The project gave me a reason to work with concepts like tsvector, plainto_tsquery, ranking, weighted searchable fields, GIN indexes, pg_trgm indexes, and query-backed result previews. Those concepts are present in the schema and search endpoint, even though the uploaded-file browser currently uses application-level filtering for its main list search.

Learning PostgreSQL through implementation

A lot of database learning happens through basic CRUD examples. PgSeek pushed past that because search immediately raises harder questions.

  • What should be searchable?
  • Which columns should be indexed?
  • How should file metadata and file contents be stored?
  • When should the database calculate rank, and when is simpler application filtering enough?
  • How should preview data be returned safely?
  • How does the schema evolve as the application grows?

Answering those questions required treating PostgreSQL like an engineering system instead of just a storage dependency. The database schema, migrations, search vectors, indexes, and API queries all had to support the product behavior together.

Why TypeScript mattered

PgSeek was also a chance to use TypeScript in a practical full-stack setting. The project uses Nuxt 4, Vue, server routes, typed data structures, and PostgreSQL-backed API responses. That combination made type safety useful in a very direct way.

Search results are structured data. Uploaded files have names, sizes, MIME types, timestamps, preview rules, and delete behavior. API routes need predictable inputs and outputs. TypeScript helped make those contracts explicit so the application could evolve without relying on guesswork.

The biggest lesson was that TypeScript is not just about catching syntax mistakes. It helps document the shape of the system. When the frontend, backend routes, and database access all move together, stronger types make refactoring safer and the code easier to reason about.

Containerization changed the development workflow

One of the most important parts of PgSeek was learning how Docker changes local development. Without containers, every service becomes tied to the host machine: local PostgreSQL versions, installed tools, environment variables, ports, and manual setup steps.

Docker made the environment reproducible. The application and PostgreSQL database could run together with a single command, using the same expected configuration each time.

Run PgSeek locally Docker Compose
docker compose up --build

That one command represents a major shift. The database, app server, ports, volumes, health checks, and startup order are no longer scattered across notes or memory. They are described as infrastructure.

Docker Compose made the system feel real

PgSeek uses Docker Compose to run the Nuxt application and PostgreSQL database as separate services. That separation made several infrastructure concepts much easier to understand.

  • The app connects to PostgreSQL through the Compose service name postgres.
  • The database uses a persistent volume so data survives container restarts.
  • Migrations are mounted into the PostgreSQL initialization flow.
  • The app waits for the database health check before starting.
  • Environment variables define runtime behavior instead of hardcoded settings.

Those details are easy to skip in small portfolio apps, but they matter in real systems. PgSeek helped turn containerization from a buzzword into something practical and understandable.

Building the application around real user workflows

The PgSeek interface was built around a simple workflow: upload files, browse stored files, search file names and searchable text content, preview safe files, and delete what is no longer needed. That gave the project a real product shape instead of being only a backend experiment.

PgSeek upload screen with a drag and drop file intake area

The backend stores uploaded files in PostgreSQL, exposes API routes for listing and searching records, supports safe previews for supported content types, and documents the API through Swagger/OpenAPI. The frontend gives those backend behaviors a polished interface instead of leaving them as isolated endpoints.

PgSeek uploaded files screen with search, filters, previews, and file management controls

That mattered because it connected database engineering, API development, frontend state, and user experience into one project.

CI and quality checks made the project stronger

PgSeek also includes the kind of quality workflow that makes a project feel more complete. The repository includes linting, tests, type checking, a production build, a Docker image build, Husky pre-commit checks, and GitHub Actions automation.

That matters because confidence does not come from hoping the app still works. It comes from repeatable checks that validate the code before changes move forward.

Local quality check pnpm
pnpm run ci:check

The Docker image build is especially important because it verifies more than application code. It confirms that the project can be packaged as a container image, even without a deployment or publishing step.

What PgSeek demonstrates

PgSeek is more than a searchable file app. It demonstrates how several software engineering skills fit together in one practical system.

  • PostgreSQL schema design, uploaded-file storage, search vectors, ranking queries, and indexing
  • TypeScript-based full-stack development with Nuxt and Vue
  • Docker Compose for repeatable multi-service development
  • Container networking, volumes, health checks, and runtime configuration
  • API design for uploads, search, previews, deletion, and OpenAPI documentation
  • Automated CI checks with Jest, ESLint, type checking, builds, Docker image validation, and GitHub Actions

The final application matters, but the larger value is the engineering process behind it. PgSeek helped me learn how databases, containers, APIs, frontend code, and automation fit together as one system.

The bigger takeaway

The best learning projects are the ones that force systems to interact. PgSeek did that. It was not only about writing a search bar or storing files. It was about learning how to build an environment where the app, database, containers, and quality checks all support each other.

Good software is not just code that runs locally. It is structured, searchable, typed, tested, containerized, documented, and reproducible.

That is what PgSeek helped me practice.