This episode covers the various ChromaDB client types: Client, PersistentClient, EphemeralClient, and HttpClient, along with how to create and retrieve collections with the right name, metadata, and embedding function parameters.

The foundation was laid in the first two episodes. Now it is time to write real code. Episode 3 is the practical gateway: we will pick the client type that fits your needs, understand how each type differs, then create your first collection with the right configuration.
One concept will keep recurring: almost every operation starts from a client. The client determines where ChromaDB talks — to memory, to a local directory, or to an HTTP server. This choice is trivial in development but crucial in production. Let's break them down one by one.
The simplest function, chromadb.Client(), automatically picks a mode based on the environment. If there is no server configuration, it behaves as an embedded in-memory client:
import chromadb
client = chromadb.Client()This client is practical for quick experiments. chromadb.Client() uses the built-in settings system — later in episode 12 we will see how it can be switched to client-server mode with environment variables.
PersistentClient is the big sibling of the default client: all data is written to disk, so it survives after the process ends. This is the main choice for long-term development and single-process embedded applications.
client = chromadb.PersistentClient(path="./data-chroma")The only difference from chromadb.Client() is the path argument, which determines the storage folder. PersistentClient(path="./data-chroma") creates the ./data-chroma folder automatically when the first collection is created.
EphemeralClient is exactly like the in-memory chromadb.Client(), but created explicitly. All data is lost when the process finishes. This is perfect for unit tests that must be clean of old data:
client = chromadb.EphemeralClient()HttpClient connects a client to a separately running ChromaDB server. This is the mode you will use in production:
client = chromadb.HttpClient(host="localhost", port=8000)Make sure the server is running before calling the code above. chromadb.HttpClient(host="localhost", port=8000) sends all operations over HTTP to the server — we will cover the full details of this mode in episode 12.
Info
Remember this pattern: in-memory for experiments, persistent for local development, ephemeral for tests, and HTTP for production. Choosing the right client from the start saves you from migrating later.
All the client types above share the same API for collections. Creating your first collection:
client = chromadb.PersistentClient(path="./data-chroma")
collection = client.create_collection(
name="artikel-tech",
metadata={"hnsw:space": "cosine"},
)The metadata argument in create_collection(name="artikel-tech", metadata={"hnsw:space": "cosine"}) is not just decoration: this is where we set the distance function used by the index. We will fully examine the hnsw:space: cosine value in episode 9.
Calling create_collection twice with the same name will error. To retrieve an existing collection, use get_collection:
collection = client.get_collection(name="artikel-tech")The recommended pattern: try get first, create if it does not exist. Because the embedded embedding function is stored inside the collection, the name and embedding function settings must be consistent between create and get.
Every collection has an embedding function. If none is specified, ChromaDB uses the DefaultEmbeddingFunction based on the ONNX MiniLM model, which runs locally — no API key, no internet. This is why ChromaDB was so easy to use in episode 0.
collection = client.create_collection(
name="artikel-tech",
embedding_function=chromadb.utils.embedding_functions.DefaultEmbeddingFunction(),
)For most of this series, the default is enough. This embedding function produces 384-dimensional vectors. When you need higher quality or a different dimensionality — for example from OpenAI or sentence-transformers — episode 6 will guide you through replacing it.
Now that the collection exists, let's fill and inspect it:
collection.add(
ids=["a1", "a2"],
documents=[
"Docker mempermudah deploy aplikasi",
"Kubernetes mengatur banyak container",
],
metadatas=[{"topik": "devops"}, {"topik": "devops"}],
)
jumlah = collection.count()
print(jumlah)The call collection.count() returns the number of items in the collection — it should print 2. Notice that embeddings do not need to be supplied because they are generated from the documents.
To see the full contents:
semua = collection.get()
print(semua["documents"])collection.get() returns all ids, documents, and metadatas as a dict — this structure will be used very often in episode 4.
Episode 3 marks the start of the practical part of this series. You now know the four client types (Client, PersistentClient, EphemeralClient, HttpClient), how to create and retrieve collections, how to set the distance function through metadata, and the role of the default ONNX MiniLM embedding function.
Key takeaways:
PersistentClient for development; HttpClient for production.create_collection errors if the name already exists; use get_collection to retrieve it.hnsw:space controls index behavior.collection.count() and collection.get() are basic inspection tools.In the next episode, episode 4, we will discuss basic CRUD — all data manipulation operations: add, get, update, upsert, delete, count, and modify, along with the combinations of ids, documents, metadatas, and embeddings you can store all at once. Your first collection is ready to be filled.