# New Data Function

> Create a new function in the data/ package following project conventions Use when this capability is needed.

- Skill: `tomevault-io/new-data-function` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/new-data-function`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/new-data-function/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/new-data-function

---


# Data Layer Conventions

The `data/` package is the **sole boundary** between TFx and the go-tfe SDK. All TFE/HCP Terraform API calls happen exclusively here. No other package should import `go-tfe` for API calls. This isolation makes the codebase easier to test and maintain.

## Pagination — Always use `client/pagination.go`

Use `client.FetchAll[T]()` for **all** paginated list operations. Never write manual pagination loops. Use `client.NewPaginationFromTFE()` to convert go-tfe pagination. PageSize should always be 100.

**Canonical example** — `data/projects.go:FetchProjects()`:

```go
func FetchThings(c *client.TfxClient, orgName string) ([]*tfe.Thing, error) {
    return client.FetchAll(c.Context, func(pageNumber int) ([]*tfe.Thing, *client.Pagination, error) {
        opts := &tfe.ThingListOptions{
            ListOptions: tfe.ListOptions{PageNumber: pageNumber, PageSize: 100},
        }
        result, err := c.Client.Things.List(c.Context, orgName, opts)
        if err != nil {
            return nil, nil, err
        }
        return result.Items, client.NewPaginationFromTFE(result.Pagination), nil
    })
}
```

## Function Naming

| Pattern | Purpose | Example |
|---------|---------|---------|
| `Fetch*` (plural) | Paginated list returning slice | `FetchProjects`, `FetchWorkspaces` |
| `Fetch*` (singular) | Read single item by ID | `FetchProject`, `FetchRun` |
| `Fetch*ByName` | Search + exact match by name | `FetchProjectByName` |
| `Get*ID` | Resolve name to ID | `GetWorkspaceID` |
| `Create*` / `Update*` / `Delete*` | State-changing operations | `CreateVariable`, `DeleteVariable` |
| `List*` | List with optional `maxItems` limit | `ListRegistryModules` |

## Function Signature

- First parameter is always `c *client.TfxClient`
- Return go-tfe types when possible (e.g., `[]*tfe.Project`)
- When aggregating across multiple API calls, define custom result types in `cmd/views/` to avoid import cycles (see `cmd/views/run_policy.go:RunPolicyResult`, `cmd/views/admin_metrics.go:MetricsWorkspace`)

## SDK Gaps — Direct HTTP Calls

When go-tfe doesn't expose a needed field, use a direct HTTP call via `c.Hostname` and `c.Token`. See `fetchEvaluationOutputs()` in `data/policy_checks.go` for the pattern. Always add a comment explaining why the raw call is necessary.

## Logging

Use `output.Get().Logger()` with structured key-value pairs:

- `Debug` — function entry/exit with parameters
- `Trace` — per-page pagination details
- `Error` — failures with full context
- `Info` — completion summaries with counts

## Error Handling

- Use `github.com/pkg/errors` — wrap with context: `errors.Wrap(err, "message")`
- Use `errors.Errorf()` for custom "not found" messages
- In multi-item loops, `log.Error` + `continue` for individual item failures (don't fail the whole batch)

## File Organization

- One file per resource: `data/projects.go`, `data/workspaces.go`
- File names use plural: `projects.go`, `variables.go`
- Integration tests co-located: `data/projects_integration_test.go`

## File Header

```go
// Copyright (c) Tom Straub (github.com/straubt1) 2025
// SPDX-License-Identifier: MIT
```

## Reference Implementations

- **List with pagination**: `data/projects.go` — `FetchProjects()`
- **Single read**: `data/runs.go` — `FetchRun()`
- **Read by name**: `data/projects.go` — `FetchProjectByName()`
- **Create/Update/Delete**: `data/variables.go` — `CreateVariable()`, `UpdateVariable()`, `DeleteVariable()`
- **Complex aggregation with custom types**: `data/policy_checks.go` — `FetchRunPolicyDetails()`
- **Direct HTTP for SDK gaps**: `data/policy_checks.go` — `fetchEvaluationOutputs()`

## Task

Create the data layer function for: $ARGUMENTS

Follow all conventions above. Use `data/projects.go` as your primary reference.

---
> Source: [straubt1/tfx](https://github.com/straubt1/tfx) — distributed by [TomeVault](https://tomevault.io).
<!-- tomevault:4.0:skill_md:2026-06-21 -->

